let redis = require('../dao/redis');
let gameDao = require('../dao/gameDao');
let userDao = require('../dao/userDao');
let clubMemberDao = require('../dao/ClubMemberDao');
let clubInfoDao = require('../dao/ClubInfoDao');
let clubCardsUpdateRecordDao = require('../dao/ClubCardsUpdateRecordDao');

let constants = require('../iconst');
let redisConst = require('../const/redisConst');
let assert = require('assert');
let iconst = require('../iconst');
let numUtil = require('../util/numUtil');
let cardUtil = require('../util/cardUtil');
let stringUtil = require('../util/stringUtil');
let objUtil = require('../util/objUtil');

let friendService = require('./friendService');
let storeService = require('./StoreService');
let roomCacheService = require('./RoomCacheService');
let router = require('../router/Router');

let mysqlPool = require('../dao/MySQLPool');


const { BusiError, SysError } = require('../exception/exception');

class GameService {
    constructor() {

    }

    async setGameInfo(key, val) {
        // constants.gameHost
        let res = await this.getGameInfo(val.gameType);
        if (res) {
            await redis.hdel(constants.gameHost, res);
        }
        let item = await redis.hset(constants.gameHost, key, val);
    }


    async getGameInfo(gameType) {
        let item = await redis.hgetall(constants.gameHost);

        let resMap = {};
        if (item) {
            for (let it in item) {
                let i = item[it];
                if (i.active === 1) {
                    resMap[i.gameType] = it;
                }
            }
        }

        console.logw(resMap);
        let res = resMap[gameType];
        return resMap[gameType];
    }

    async getGameRecord(uid, page) {
        let cnt = await gameDao.queryPageCnt(uid);
        if (cnt === 0) {
            return { cnt: 0, data: [] };
        }

        let data = await gameDao.queryPage(uid, page);
        return { cnt: cnt, data: data };
    }

    async queryDeskInfos(gameType, roomType, count, uid) {
        let lockKey = redisConst.GAME_DESK_INFO_LIMIT + ":" + gameType + ":" + roomType + ":" + uid;
        let res = await redis.lock(lockKey, 1, 1);
        if (!res) {
            throw new BusiError(500, "query desk infos too many");
        }

        let cacheKey = redisConst.GAME_DESK_INFO + ":" + gameType + ":" + roomType;
        let cacheKey2 = redisConst.GAME_DESK_PWD + ":" + gameType + ":" + roomType;
        try {
            let res = await redis.hscan(cacheKey, count);
            let res2 = await redis.hscan(cacheKey2, count);
            let map = {};
            for (let i = 0; i < res2.length; i++) {
                let item = res2[i];
                map[item.roomId] = item;
            }

            for (let i = 0; i < res.length; i++) {
                let item = res[i];
                let pwdItem = map[item.roomId];
                if (pwdItem && (new Date().getTime() - pwdItem.timestamp) / 1000 / 60 / 60 < 24) {
                    item.isNeedPwd = 1;
                    item.owner = pwdItem.owner;
                }
            }
            return res;
        } catch (e) {
            console.logw("query desk infos err", e);
            throw new BusiError(500, "query desk infos err");
        }
    }

    /**
     *
     * @param {String} gameType
     * @returns {Promise<void>}
     */
    async clearDeskInfos(gameType) {
        let cacheKey1 = redisConst.GAME_DESK_INFO + ":" + gameType + ":" + 1;
        let cacheKey2 = redisConst.GAME_DESK_INFO + ":" + gameType + ":" + 2;
        let cacheKey3 = redisConst.GAME_DESK_INFO + ":" + gameType + ":" + 3;
        let cacheKey4 = redisConst.GAME_DESK_INFO + ":" + gameType + ":" + 4;
        try {
            await redis.del(cacheKey1);
            await redis.del(cacheKey2);
            await redis.del(cacheKey3);
            await redis.del(cacheKey4);
        } catch (e) {
            console.logw("clear desk infos err", e);
            throw new BusiError(500, "clear desk infos err");
        }

        let pCacheKey1 = redisConst.GAME_DESK_PWD + ":" + gameType + ":" + 1;
        let pCacheKey2 = redisConst.GAME_DESK_PWD + ":" + gameType + ":" + 2;
        let pCacheKey3 = redisConst.GAME_DESK_PWD + ":" + gameType + ":" + 3;
        let pCacheKey4 = redisConst.GAME_DESK_PWD + ":" + gameType + ":" + 4;
        try {
            await redis.del(pCacheKey1);
            await redis.del(pCacheKey2);
            await redis.del(pCacheKey3);
            await redis.del(pCacheKey4);
        } catch (e) {
            console.logw("clear desk pwd err", e);
            throw new BusiError(500, "clear desk pwd err");
        }
    }

    /**
     *
     * @param deskInfo
     * @param {String} deskInfo.gameType
     * @param {String} deskInfo.roomType
     * @param {String} deskInfo.roomId
     * @param {String} deskInfo.pwd
     * @param {String} deskInfo.info
     * @returns {Promise<void>}
     */
    async setDeskInfos(deskInfo) {
        let cacheKey = redisConst.GAME_DESK_INFO + ":" + deskInfo.gameType + ":" + deskInfo.roomType;
        let pCacheKey = redisConst.GAME_DESK_PWD + ":" + deskInfo.gameType + ":" + deskInfo.roomType;
        try {
            let pwd = deskInfo.pwd;
            delete deskInfo.pwd;
            await redis.hset(cacheKey, deskInfo.roomId, deskInfo.info);
            if (pwd && pwd !== "") {
                await redis.hset(pCacheKey, deskInfo.roomId, pwd);
            } else {
                await redis.hdel(pCacheKey, deskInfo.roomId);
            }
        } catch (e) {
            console.logw("set desk infos err", e);
            throw new BusiError(500, "set desk infos err");
        }
    }

    /**
     *
     * @param deskInfo
     * @param {String} deskInfo.gameType
     * @param {String} deskInfo.roomType
     * @param {String} deskInfo.roomId
     * @param {String} deskInfo.pwd
     * @param {String} deskInfo.owner
     * @returns {Promise<void>}
     */
    async setDeskPwd(deskInfo) {
        let cacheKey = redisConst.GAME_DESK_PWD + ":" + deskInfo.gameType + ":" + deskInfo.roomType;
        try {
            let res = await redis.hget(cacheKey, deskInfo.roomId);
            if (res) { // 房间密码有效时间为1天
                assert((new Date().getTime() - res.timestamp) / 1000 / 60 / 60 >= 24, "room pwd is exists!");
            }

            res = await redis.hset(cacheKey, deskInfo.roomId, {
                timestamp: new Date().getTime(),
                pwd: deskInfo.pwd,
                owner: deskInfo.owner,
                roomId: deskInfo.roomId
            });
        } catch (e) {
            console.logw("set desk pwd err", e);
            throw new BusiError(500, "set desk pwd err");
        }
    }

    /**
     *
     * @param deskInfo
     * @param {String} deskInfo.gameType
     * @param {String} deskInfo.roomType
     * @param {String} deskInfo.roomId
     * @param {String} deskInfo.pwd
     * @returns {Promise<void>}
     */
    async validDeskPwd(deskInfo) {
        let cacheKey = redisConst.GAME_DESK_PWD + ":" + deskInfo.gameType + ":" + deskInfo.roomType;
        try {
            let res = await redis.hget(cacheKey, deskInfo.roomId);
            if (res && (new Date().getTime() - res.timestamp) / 1000 / 60 / 60 >= 24) { // 房间密码有效时间为1天
                assert(res.pwd.toString() === deskInfo.pwd, "room pwd is not correct!");
            }
        } catch (e) {
            console.logw("valid desk pwd err", e);
            throw new BusiError(500, "valid desk pwd err");
        }
    }

    /**
     *
     * @param deskInfo
     * @param {String} deskInfo.gameType
     * @param {String} deskInfo.roomType
     * @param {String} deskInfo.roomId
     * @param {String} deskInfo.pwd
     * @returns {Promise<Boolean>}
     */
    async isDeskNeedPwd(deskInfo) {
        let cacheKey = redisConst.GAME_DESK_PWD + ":" + deskInfo.gameType + ":" + deskInfo.roomType;
        try {
            let res = await redis.hget(cacheKey, deskInfo.roomId);
            if (res && (new Date().getTime() - res.timestamp) / 1000 / 60 / 60 >= 24) {
                return true;
            }
        } catch (e) {
            console.logw("valid desk pwd err", e);
            throw new BusiError(500, "valid desk pwd err");
        }

        return false;
    }

    /**
     *
     * @param info
     * @param {String} info.uid
     * @param {String} info.gameType
     * @param {String} info.info
     * @returns {Promise<void>}
     */
    async appendUserGameResult(info) {
        let cacheKey = redisConst.USER_GAME_RESULT + ":" + info.uid + ":" + info.gameType;
        try {
            let res = await redis.lpush(cacheKey, info.info);
            await redis.ltrim(cacheKey, 0, 9);
            if (!res) {
                throw new BusiError(500, "append user game info err");
            }
        } catch (e) {
            console.logw("append user game info err:", info);
            throw e;
        }
    }

    /**
     *
     * @param info
     * @param {String} info.uid
     * @param {String} info.gameType
     * @returns {Promise<void>}
     */
    async queryUserGameResult(info) {
        try {
            let cacheKey = redisConst.USER_GAME_RESULT + ":" + info.uid + ":" + info.gameType;
            let res = await redis.lrange(cacheKey, 0, 9);
            return res;
        } catch (e) {
            console.logw("append user game info err:", info);
            throw e;
        }
    }

    /**
     * 房间存活检测
     * @param {String} roomID 
     * @param {String} gt
     */
    async checkFkRoomInfo(roomID, gt) {
        let res2 = await roomCacheService.getRoom(roomID); // 缓存最长保留时间为24h
        if (!res2) {
            throw new BusiError(500, "该房间不存在或者已经被销毁");
        }

        if (res2.gt !== gt) {
            throw new BusiError(500, "游戏类型与该房间游戏类型不匹配");
        }

        await roomCacheService.checkRoom(roomID);
        return res2;
    }

    /**
    * 
    * @param {String} roomID 
    * @param {String} gt 
    * @param {Number} ts 
    * @param {Number} gc 
    */
    async setFkRoomGameFlag(roomID, gt, ts, gc) {
        await roomCacheService.setRoomGameFlag(roomID, gt, ts, gc);
    }

    /**
     * 
     * @param {String} roomID 
     * @param {String} gt 
     * @param {Number} ts 
     */
    async destroyFkRoom(roomID, gt, ts,gameCount) {
        let res2 = await roomCacheService.getRoom(roomID); // 缓存最长保留时间为24h
        console.logw("解散房间-房间信息：");
        console.logw(res2)
        if(gameCount==0)
        {
            console.logw("游戏局数为0 需要返回房卡",roomID, gt, ts,gameCount)
            await this.returnCardByNotify(res2,roomID);
        }else if (gameCount>0){
            if(res2==null)
            {
                let cacheRoomInforecreate =await  roomCacheService.getRecreateLockInfo(roomID);
                if(!cacheRoomInforecreate)
                {
                    console.logw("获取recreate 房间信息失败 ",roomID);
                }else{
                    if (cacheRoomInforecreate.cards){
                        console.log("do save fangka record in recreate!");
                        await userDao.saveFkRechargeRecord(cacheRoomInforecreate.cards, gt);
                    }
                }

            }else {
                if (res2.cards) {
                    console.log("do save fangka record!")
                    await userDao.saveFkRechargeRecord(res2.cards, gt);
                } else {
                    console.logw("returnCardByNotify 未发现房卡配置:", res2.cards);
                }
            }
        }else{
            console.log("gamecount is invaild!");
        }
        if (!res2) {
            console.logw("该房间不存在或者已经被销毁")

            throw new BusiError(500, "该房间不存在或者已经被销毁");
        }

        if(res2.linkRoomId)
        {
           // console.logg("删除recreate 缓存信息 ",res.linkRoomId);
          //  await  roomCacheService.delRecreateLockInfo(res2.linkRoomId)
        }

        if (res2.gt !== gt) {
            let res = await roomCacheService.isRoomAlive(roomID);
            if (res) {
                throw new BusiError(500, "游戏类型与该房间游戏类型不匹配");
            }
        }

        if (res2.ts !== ts) {
            throw new BusiError(500, "游戏ts与该房间游戏ts不匹配");
        }

        // let creatorCacheKey = redisConst.GAME_ROOM_CREATOR_FLAG + ":" + res2.owner;
        // await redis.del(creatorCacheKey);

        await roomCacheService.removeRoom(roomID);
        await roomCacheService.removeRoomAliveFlag(roomID);

        let playRes = await roomCacheService.getRoomGameFlag(roomID, gt, ts);
        if (!playRes) {
            //this.returnCardProcess(res2);
        }

        // 如果是俱乐部房间, 顺便清理俱乐部房间
        if(res2.clubID){
            roomCacheService.removeClubRoom(res2.clubID, roomID);
            try{
                if (res2.clubID) {
                    let data = { event: iconst.netEvent.destroyRoom, clubId: res2.clubID, roomId: roomID };
                    console.logw("发送解散房间时时通知 ", data);
                    router.rpcIo.sendMsg("niuniuClub", data);
                }
            } catch (e) {
                console.logw('解析 roomInfo 失败 ', e.message);
            }
        }

        // 清空房间玩家列表
        roomCacheService.clearRoomUsersMap(gt, roomID);
        //let uuid = 'room_' + roomID;
        //let roomInfo = await redis.getHashItem(iconst.gameHistorySytx, uuid);

        return res2;
    }

    /**
     * 
     * @param {String} uid 
     */
    async getUserRoomInfo(uid) {
        // let creatorCacheKey = redisConst.GAME_ROOM_CREATOR_FLAG + ":" + uid;
        // let creatorInfo = await redis.get(creatorCacheKey);
        // if (creatorInfo) {
        //     let gameRoomInfoCacheKey = redisConst.GAME_ROOM_FLAG + ":" + creatorInfo.roomID;
        //     let res2 = await redis.get(gameRoomInfoCacheKey);
        //     if (res2 && res2.owner === uid) {
        //         // 房间是否有效
        //         let roomTickerCacheKey = redisConst.GAME_ROOM_TICKER_FLAG + ":" + creatorInfo.roomID;
        //         let rti = await redis.get(roomTickerCacheKey);
        //         if (rti) {
        //             return creatorInfo;
        //         }
        //     }
        // }

        let roomInfo = await roomCacheService.getUserRoom(uid);
        if (!roomInfo) {
            throw new BusiError(500, "玩家没有游戏房间信息");
        }

        // 房间是否存在
        let res2 = await roomCacheService.getRoom(roomInfo.roomID);
        if (!res2) {
            console.logw("游戏房间不存在或已经被销毁, 清除玩家房间信息:", roomInfo);
            await roomCacheService.removeUserRoom(uid);
            throw new BusiError(500, "游戏房间不存在或已经被销毁");
        }

        // 房间是否有效
        let rti = await roomCacheService.isRoomAlive(roomInfo.roomID);
        if (!rti) {
            console.logw("游戏房间已经失效, 清除玩家房间信息:", roomInfo);
            await roomCacheService.removeUserRoom(uid);
            throw new BusiError(500, "游戏房间已经失效");
        }

        return roomInfo;
    }
    //根据房间信息重现创建房间
    async reCreateFkGameRoom(uid, roomId) {

        var  roomInfo=await roomCacheService.getRecreateRoom(roomId);
        if(!roomInfo)
        {
            console.logg("上一把游戏的创建参数不存在，不能创建房间")
            throw new BusiError(500, "上一把游戏的创建参数不存在，不能创建房间");
            return;
        }

        var   lockRoomInfo=await roomCacheService.getRecreateLockInfo(roomId);
        if(lockRoomInfo)
        {
            console.logg("从缓存信息中拿到房间信息并返回 ",roomId,lockRoomInfo.roomId)
            return lockRoomInfo;
        }
        console.logg("执行一键开房逻辑 ",uid,roomId)
        await roomCacheService.setRecreateLockInfo(roomId,roomInfo);
        roomInfo.data.linkRoomId=roomId;
        if(roomInfo.unionID)
        {
            var roomInfo=await  this.createUnionGameRoom(uid,roomInfo.gt,roomInfo.unionID,roomInfo.data);
            await roomCacheService.setRecreateLockInfo(roomId,roomInfo);
            console.logg("联盟开房后 ",roomId,roomInfo.roomId);
            return  roomInfo;
        }
        var roomInfo=await  this.createFkGameRoom(uid,roomInfo.gt,roomInfo.clubID,roomInfo.data);
        await roomCacheService.setRecreateLockInfo(roomId,roomInfo);
        console.logg("开房后房间号 ",roomId,roomInfo.roomId);
        return  roomInfo;
    }
    /**
     * 创建房卡房间
     * @param {String} uid 
     * @param {String} gameType 
     * @param {String} clubID
     * @param {Object} data 
     */
    async createFkGameRoom(uid, gameType, clubID, data) {
        // 该用户是否在其他游戏中
        // await this.getGameFlagGaming(uid, gameType);
        console.logw("创建房间，房间类型是:", gameType);
        let gameTickerKey = redisConst.GAME_TICKER_FLAG + ":" + gameType;
        let resT = await redis.get(gameTickerKey);
        if (!resT) {
           // throw new BusiError(500, "当前游戏维护中，请选择其他游戏");
        }

        let cardsFrom = 0;
        let clubInfo = null;
        if (clubID && clubID !== ''){
            clubInfo = await clubInfoDao.getByClubID(clubID);
            if(!clubInfo){
                throw new BusiError(500, "创建俱乐部房间失败, 俱乐部不存在");
            }

            let clubMember = await clubMemberDao.getByUID(uid, clubID);
            if(!clubMember){
                throw new BusiError(500, "创建俱乐部房间失败");
            }

            if (clubMember.setting && clubMember.setting !== ''){
                let setting = JSON.parse(clubMember.setting);
                if(setting){
                    if(setting.roomMode === 1){
                        if(String(clubMember.ownerUid) !== String(clubMember.memberUid)){
                            throw new BusiError(500, "该俱乐部只允许群主创建房间");
                        }
                    }
                    
                    if(setting.feeMode === 1){
                        cardsFrom = 1;
                    }
                }
            }
        }

        // 创建房间
        // 生成房间号
        let roomID = "";
        for (let i = 0; i < 3; i++) {
            let randomID = numUtil.random(100000, 999999);
            let rti = await roomCacheService.isRoomAlive(randomID);
            if (rti) {
                continue;
            }

            roomID = randomID + "";
            break;
        }

        if (roomID === '') {
            throw new BusiError(500, "创建房间失败, 请稍后再试");
        }

        //修改
        let cards = 0;
        if (clubID != '5740934'){
            cards = cardUtil.calcCard(gameType, data.turnOfGame, data.playerNum);
            if (cards === 0) {
                throw new BusiError(500, "创建房间失败, 当前游戏房卡信息未配置");
            }
        }else{
            cardsFrom = 1;
        }

        // if (cards === 0) {
        //     throw new BusiError(500, "创建房间失败, 当前游戏房卡信息未配置");
        // }

        let user = await userDao.getUserByUid(uid);
        if(!user){
            throw new BusiError(500, "用户信息不存在");
        }

        if(cardsFrom === 0){
            if (user.cardNum < cards) {
                throw new BusiError(500, "创建房间失败, 房卡余额不足, 请及时充值");
            }
        }

        if(cardsFrom === 1){
            if(clubInfo.clubCard < cards){
                throw new BusiError(500, "创建房间失败, 俱乐部房卡基金不足, 请及时充值");
            }
        }
        
        // 创建房间
        let res = await roomCacheService.setRoomAliveNx(roomID);
        if (!res) {
            throw new BusiError(500, "创建房间失败, 请稍后再试[1]");
        }
        // if(data.mode==1)
        // {
        //     console.logg("金币豆模式,入场分最低是底分的50倍 ",data.limitCoins,data.score)
        //     if(data.limitCoins<data.score*50)
        //     {
        //         data.limitCoins=data.score*50;
        //         console.logg("动态调整入场分为底分的50倍 ",data.limitCoins,data.score)
        //
        //     }
        // }
        // 房间信息
        let cacheRoomInfo = {
            roomID: roomID,
            owner: uid + "",
            ownerHeadImgURL: user.headimgurl,
            gt: gameType,
            cards: cards,
            data: data,
            ts: new Date().getTime(),
            cardsFrom: cardsFrom
        };

        if (!stringUtil.isBlank(clubID)){
            cacheRoomInfo.clubID = clubID;
            let res = await roomCacheService.setClubRoom(clubID, roomID, cacheRoomInfo);
            if (!res) {
                await roomCacheService.removeRoomAliveFlag(roomID);
                throw new BusiError(500, "创建房间失败, 请稍后再试[4]");
            }
        }

        //代理房间设置标志
        if(cacheRoomInfo.clubID && cacheRoomInfo.clubID === '5740934'){
            cacheRoomInfo.agent = 1;
        }

        // 保存房间信息
        let res2 = await roomCacheService.setRoom(roomID, cacheRoomInfo);
        if (!res2) {
            await roomCacheService.removeRoomAliveFlag(roomID);
            throw new BusiError(500, "创建房间失败, 请稍后再试[2]");
        }

        // 保存房主信息
        // let res3 = await redis.setNx(creatorCacheKey, cacheRoomInfo, 60 * 60 * 24); // 缓存最长保留时间为24h
        // if (!res3){
        //     await redis.del(roomTickerCacheKey);
        //     await redis.del(gameRoomInfoCacheKey);
        //     throw new BusiError(500, "创建房间失败, 请稍后再试[3]");
        // }

        // 从个人扣除房卡
        if (cardsFrom === 0){
            console.logw("扣除房卡560 ",uid,cards)
            let decCardRes = await userDao.decCard(uid, cards);
            if (decCardRes <= 0) {
                // await redis.del(creatorCacheKey);
                await roomCacheService.removeRoomAliveFlag(roomID);
                await this.removeRoom(roomID);
                if(cacheRoomInfo.clubID){
                    await roomCacheService.removeClubRoom(cacheRoomInfo.clubID, roomID);
                }
                throw new BusiError(500, "扣除房卡失败, 创建房间失败");
            }
            // await userDao.saveFkRechargeRecord(cards, gameType);
        }

        // 从俱乐部扣除房卡
        if(cardsFrom === 1){
            console.logw("扣除房卡576 ",uid,cards)
            let decCardRes = await clubInfoDao.upateCards(clubID, -cards);
            if (decCardRes <= 0) {
                // await redis.del(creatorCacheKey);
                await roomCacheService.removeRoomAliveFlag(roomID);
                await this.removeRoom(roomID);
                if(cacheRoomInfo.clubID){
                    await roomCacheService.removeClubRoom(cacheRoomInfo.clubID, roomID);
                }
                throw new BusiError(500, "扣除房卡失败, 创建房间失败");
            }
            // await userDao.saveFkRechargeRecord(cards, gameType);
            await clubCardsUpdateRecordDao.save({club_id: clubID, c_uid: uid, cards: -cards, type: "CREATE_ROOM", remark: gameType});
        }
        
        // 清空房间玩家列表
        roomCacheService.clearRoomUsersMap(gameType, roomID);

        let self = this;
        //傻逼 用法直接注释
        // setTimeout(() => {
        //     self.returnCardProcess(cacheRoomInfo);
        // }, 1000 * 60 * 5 + 1000 * 2);

        let info = objUtil.clone(cacheRoomInfo);
        info.playWay = JSON.stringify(cacheRoomInfo.data);
        delete info.data;
        
        // 存储房间信息
        storeService.saveRoomInfo(info);

        return cacheRoomInfo;
    }


    //add
    /**
     * 创建联盟房间
     * @param {String} uid
     * @param {String} gameType
     * @param {String} clubID
     * @param {String} unionID
     * @param {Object} data
     */
    async createUnionGameRoom(uid, gameType,unionID, data) {
        // 该用户是否在其他游戏中
        // await this.getGameFlagGaming(uid, gameType);
        console.logw("创建房间，房间类型是:", gameType);
        let gameTickerKey = redisConst.GAME_TICKER_FLAG + ":" + gameType;
        let resT = await redis.get(gameTickerKey);
        if (!resT) {
            //throw new BusiError(500, "当前游戏维护中，请选择其他游戏");
        }

        // let clubInfo = null;
        // if (clubID && clubID !== ''){
        //     clubInfo = await clubInfoDao.getByClubID(clubID);
        //     if(!clubInfo){
        //         throw new BusiError(500, "创建俱乐部房间失败, 俱乐部不存在");
        //     }
        //
        //     let clubMember = await clubMemberDao.getByUID(uid, clubID);
        //     if(!clubMember){
        //         throw new BusiError(500, "创建俱乐部房间失败");
        //     }
        // }

        // 创建房间
        // 生成房间号
        let roomID = "";
        for (let i = 0; i < 3; i++) {
            let randomID = numUtil.random(100000, 999999);
            let rti = await roomCacheService.isRoomAlive(randomID);
            if (rti) {
                continue;
            }

            roomID = randomID + "";
            break;
        }

        if (roomID === '') {
            throw new BusiError(500, "创建房间失败, 请稍后再试");
        }

        let cards = cardUtil.calcCard(gameType, data.turnOfGame, data.playerNum);
        if (cards === 0) {
            throw new BusiError(500, "创建房间失败, 当前游戏房卡信息未配置");
        }

        let union = await userDao.getUnionById(unionID);

        let fkUid = union.ownerUid;
        if(union.room_type === 2){
            fkUid = uid;
            //判断用户有没有冻结
        }
        let user = await userDao.getUserByUid(fkUid);
        /*let user = await userDao.getUnionUserByUid(uid,unionID);
        if(!user){
            throw new BusiError(500, "您没有开房权限，请联系管理员开房");
        }*/

        let cardsFrom = 0;
        if(cardsFrom === 0){
            if (user.cardNum < cards) {
                throw new BusiError(500, "创建房间失败, 房卡余额不足, 请及时充值");
            }
            let decCardRes = await userDao.decCard(fkUid, cards);
            if (decCardRes <= 0) {
                // await redis.del(creatorCacheKey);
                await roomCacheService.removeRoomAliveFlag(roomID);
                //await this.removeRoom(roomID);
                /*if(cacheRoomInfo.clubID){
                    await roomCacheService.removeClubRoom(cacheRoomInfo.clubID, roomID);
                }*/
                throw new BusiError(500, "扣除房卡失败, 创建房间失败");
            }
            // await userDao.saveFkRechargeRecord(cards, gameType);
        }

        // if(cardsFrom === 1){
        //     if(clubInfo.clubCard < cards){
        //         throw new BusiError(500, "创建房间失败, 俱乐部房卡基金不足, 请及时充值");
        //     }
        // }

        // 创建房间
        let res = await roomCacheService.setRoomAliveNx(roomID);
        if (!res) {
            throw new BusiError(500, "创建房间失败, 请稍后再试[1]");
        }

        if(data.limitCoins === undefined){
            data.limitCoins = 200 ;
        }


            // console.logg("大联盟模式,入场分最低是底分的50倍 ",data.limitCoins,data.score)
            // if(data.limitCoins<data.score*50)
            // {
            //     data.limitCoins=data.score*50;
            //     console.logg("大联盟动态调整入场分为底分的50倍 ",data.limitCoins,data.score)
            //
            // }


        // 房间信息
        let cacheRoomInfo = {
            roomID: roomID,
            owner: fkUid + "",
            ownerHeadImgURL: user.headimgurl,
            gt: gameType,
            cards: cards,
            data: data,
            ts: new Date().getTime(),
            cardsFrom: cardsFrom,
//            clubID: clubID,
            unionID: unionID,
        };

        let res5 = await roomCacheService.setUnionRoom(unionID, roomID, cacheRoomInfo);
        if (!res5) {
            await roomCacheService.removeRoomAliveFlag(roomID);
            throw new BusiError(500, "创建房间失败, 请稍后再试[4]");
        }

        // 保存房间信息
        let res2 = await roomCacheService.setRoom(roomID, cacheRoomInfo);
        if (!res2) {
            await roomCacheService.removeRoomAliveFlag(roomID);
            throw new BusiError(500, "创建房间失败, 请稍后再试[2]");
        }


        // 清空房间玩家列表
        roomCacheService.clearRoomUsersMap(gameType, roomID);

        let self = this;
        //傻逼设计，直接注释
        // setTimeout(() => {
        //     self.returnCardProcess(cacheRoomInfo);
        // }, 1000 * 60 * 5 + 1000 * 2);   //5分两秒判断是否需要返回房卡

        let info = objUtil.clone(cacheRoomInfo);
        info.playWay = JSON.stringify(cacheRoomInfo.data);
        delete info.data;

        // 存储房间信息
        storeService.saveRoomInfo(info);

        return cacheRoomInfo;
    }

    /**
     * 
     * @param {String} clubID 
     */
    async queryClubRoom(clubID){
        return roomCacheService.queryClubRoom(clubID);
    }

    async queryUnionRoom(unionID){
        return roomCacheService.queryUnionRoom(unionID);
    }

    async insertErrorInfo(uid,roomid,clubid,gametype,info,ts){
        return await clubInfoDao.insertErrorInfo(uid,roomid,clubid,gametype,info,ts);
    }

    async joinClubRoom(clubID){
        let list = await roomCacheService.queryClubRoom(clubID);

        for(let i = 0; i < list.length; i++){
            let roomInfo = list[i];
            let roomUsersList = await roomCacheService.getRoomUsersMap(roomInfo.gt, roomInfo.roomID);
            if(roomUsersList.length < roomInfo.data.playerNum){
                return roomInfo;
            }
        }

        return null;
    }

    async joinUnionRoom(unionID){
        let list = await roomCacheService.queryUnionRoom(unionID);

        for(let i = 0; i < list.length; i++){
            let roomInfo = list[i];
            let roomUsersList = await roomCacheService.getRoomUsersMap(roomInfo.gt, roomInfo.roomID);
            if(roomUsersList.length < roomInfo.data.playerNum){
                return roomInfo;
            }
        }

        return null;
    }


    /**
     * 
     * @param {{roomID:String, owner: String, gt: String, cards: Number, data: Object, ts: Number}} cacheRoomInfo 
     */

    async returnCardByNotify(cacheRoomInfo,roomId) {

        if(cacheRoomInfo==null)
        {
            cacheRoomInfo=await  roomCacheService.getRecreateLockInfo(roomId)
            if(!cacheRoomInfo)
            {
                console.logw("获取recreate 房间信息失败 ",roomId)
                return;
            }

        }
        var  cards=cacheRoomInfo.cards;
        if (cacheRoomInfo.cards) {

            // 退回给个人
            if(cacheRoomInfo.cardsFrom === 0){
                let addCardRes = await userDao.addCard(cacheRoomInfo.owner, cacheRoomInfo.cards);
                if (addCardRes <= 0) {
                    console.logw("returnCardByNotify 房卡返还失败:", cards,cacheRoomInfo);
                } else {
                    // await userDao.saveFkRechargeRecord(0-cacheRoomInfo.cards, cacheRoomInfo.gt);
                    console.logw("returnCardByNotify 房卡返还成功:", cards,cacheRoomInfo);
                }
            }

            // 退回给俱乐部
            if(cacheRoomInfo.cardsFrom === 1){
                let addCardRes = await clubInfoDao.upateCards(cacheRoomInfo.clubID, cacheRoomInfo.cards);
                if (addCardRes <= 0) {
                    console.logw("returnCardByNotify 房卡返还俱乐部失败:", cards,cacheRoomInfo);
                } else {
                    await clubCardsUpdateRecordDao.save({club_id: cacheRoomInfo.clubID, c_uid: cacheRoomInfo.owner, cards: cacheRoomInfo.cards, type: "CREATE_ROOM_RETURN", remark: cacheRoomInfo.gt});
                    console.logw(" returnCardByNotify 房卡返还俱乐部成功:", cards,cacheRoomInfo);
                }
            }

        } else {
            console.logw("returnCardByNotify 未发现房卡配置:", cacheRoomInfo);
        }


    }
    //房间被销毁了不返回房卡
    //房间超过
    //陈文杰傻逼写的东西，直接弃用
    async returnCardProcess(cacheRoomInfo) {
        console.logw("xxx  cwj")
        return;
        let rti = await roomCacheService.isRoomAlive(cacheRoomInfo.roomID);
        //房间存活的时候不进行返还操作
        if (!rti) {
            let isCardsReturn = await roomCacheService.isCardsReturn(cacheRoomInfo.roomID, cacheRoomInfo.gt, cacheRoomInfo.ts, cacheRoomInfo.owner);
            if (isCardsReturn) {
                console.logw("检测到房卡已经返还, 无需再返还:", cacheRoomInfo);
                return;
            }

            //s设置房卡返还标识，5分钟
            let setRes = await roomCacheService.setCardsReturnFlag(cacheRoomInfo.roomID, cacheRoomInfo.gt, cacheRoomInfo.ts, cacheRoomInfo.owner);
            if (setRes) {
                if (cacheRoomInfo.cards) {
                    // 退回给个人
                    if(cacheRoomInfo.cardsFrom === 0){
                        let addCardRes = await userDao.addCard(cacheRoomInfo.owner, cacheRoomInfo.cards);
                        if (addCardRes <= 0) {
                            console.logw("房卡返还失败:", cacheRoomInfo);
                        } else {
                            await userDao.saveFkRechargeRecord(0-cacheRoomInfo.cards, cacheRoomInfo.gt);
                            console.logw("房卡返还成功:", cacheRoomInfo);
                        }
                    }

                    // 退回给俱乐部
                    if(cacheRoomInfo.cardsFrom === 1){
                        let addCardRes = await clubInfoDao.upateCards(cacheRoomInfo.clubID, cacheRoomInfo.cards);
                        if (addCardRes <= 0) {
                            console.logw("房卡返还俱乐部失败:", cacheRoomInfo);
                        } else {
                            await clubCardsUpdateRecordDao.save({club_id: cacheRoomInfo.clubID, c_uid: cacheRoomInfo.owner, cards: cacheRoomInfo.cards, type: "CREATE_ROOM_RETURN", remark: cacheRoomInfo.gt});
                            console.logw("房卡返还俱乐部成功:", cacheRoomInfo);
                        }
                    }
                    
                } else {
                    console.logw("未发现房卡配置:", cacheRoomInfo);
                }

                return;
            }

            console.logw("房卡返还失败, 设置标识失败:", cacheRoomInfo);
        }
    }

    /**
     * 
     * @param {String} roomID 
     * @param {Number} ts
     * @param {String} gt
     */
    async getFkGameRoomInfo(roomID, ts, gt, uid) {
        // 获取房间信息
        let res2 = await roomCacheService.getRoom(roomID);
        if (res2) {
            let rti = await roomCacheService.isRoomAlive(roomID);
            if (rti) {
                let userInfos = await roomCacheService.getRoomUsersMap(gt, roomID);
                res2.users = userInfos;
                res2.playInfo = {
                    gc: 0
                };

                let playInfo = await roomCacheService.getRoomGameFlag(res2.roomID, res2.gt, res2.ts);
                if(playInfo){
                    res2.playInfo = playInfo;
                }

                if(res2.data && res2.data.onlyFriend === 1 && uid){
                    let isFriend = await friendService.isFriend(uid, res2.owner);
                    if(!isFriend){
                        throw new BusiError(502, "该房间[" + roomID + "]仅限房主好友加入", res2);
                    }
                }

                return res2;
            }
        }

        let record = null;
        if (ts && gt) {
            // 220884_fkniuniu17_1563270109353
            try {
                record = await storeService.querySettlement({roomID: roomID, gameType: gt, roomTs: Number(ts)});
            } catch (e) {
                console.logw("get fk game record err:", e);
            }
        }

        if (!record || !record.userGameResults) {
            throw new BusiError(500, "未发现该房间信息:" + roomID);
        }

        throw new BusiError(501, "游戏已经结束", record);
    }

    /**
     * 房间心跳消息, 2分钟心跳一次
     * @param {String} roomID 
     * @param {String} gt
     */
    async roomTicker(roomID, gt) {
        // 房间心跳, 每次延长房间5分钟
        let res2 = await roomCacheService.getRoom(roomID); // 缓存最长保留时间为24h
        if (res2 && res2.gt === gt) {
            let res = await roomCacheService.setRoomAliveEx(roomID); // 延长房间5分钟有效时间
            if (!res) {
                throw new BusiError(500, "游戏房间心跳信息设置失败");
            }
            //连表添加信息
            let tick=new Date().getTime();
            await  redis.setHashItem(redisConst.gamingMap,roomID,tick);
        }
    }

    async serverUpdatePlayNum(playNum, gt) {
        await roomCacheService.setGameUserCount(gt, playNum);
    }

    async upateUsersBuffer(uid, buffer){
        let res = await userDao.upateUsersBuffer(uid, buffer);
        if(res <= 0){
            console.logw("更新buffer失败:", uid, buffer);
            throw new BusiError(500, "更新buffer失败");
        }

        console.logw("更新buffer成功:", uid, buffer);
    }

    /**
     * 玩家进入房间接口
     * @param {String} uid 
     * @param {String} roomID 
     * @param {String} gt
     */
    async enterRoom(uid, roomID, gt, clubID) {
        // 房间是否存在
        let res2 = await roomCacheService.getRoom(roomID); // 缓存最长保留时间为24h
        if (!res2) {
            throw new BusiError(500, "该房间不存在或者已经被销毁");
        }

        //如果房间只允许好友进入，那么需要判断房主和玩家是否是好友关系
        if (res2.data && res2.data.onlyFriend === 1) {
            // let bFriend = await friendService.isFriend(uid,res2.owner);
            // if(bFriend === 1){
            //     //throw new BusiError(500, "不是好友不能进入好友房间");
            // }
        }

        // 如果是大联盟房间，则需要鉴别用户是否大联盟成员
        // if (res2.unionID){
        //     let isFrost = await clubMemberDao.getFrostByUID(uid, res2.unionID);
        //     if(isFrost){
        //         throw new BusiError(500, "账号已经被冻结，请联系管理员");
        //     }
        //     let member = await clubMemberDao.getUnionByUID(uid, clubID, res2.unionID);
        //     if(!member){
        //         //如果不正确，说明玩家走链接进游戏,这个时候要选择一个币足够的俱乐部让玩家加入联盟里的游戏
        //         let member2 = await clubMemberDao.getEnoughUnionByUID(uid,res2.unionID,res2.data.limitCoins);
        //         if(!member2){
        //             throw new BusiError(500, "疲劳值不足或非大联盟成员不能加入此房间");
        //         }
        //         if(res2.gt == "fkpj"){
        //             if(member2.unionCoin < res2.data.score * 8){
        //                 throw new BusiError(500, "疲劳值不足不能加入牌九房间" + " " + member2.unionCoin.toString());
        //             }
        //         } else if(res2.gt == "fkmj"){
        //             if(member2.unionCoin < res2.data.score * 30){
        //                 throw new BusiError(500, "疲劳值不足不能加入麻将房间" + " " + member2.unionCoin.toString());
        //             }
        //         } else if(member2.unionCoin < res2.data.limitCoins){
        //             throw new BusiError(500, "疲劳值不足不能加入此房间");
        //         }
        //
        //         res2.member = member2;
        //     }else{
        //         if(res2.gt == "fkpj"){
        //             if(member.unionCoin < res2.data.score * 8){
        //                 throw new BusiError(500, "疲劳值不足不能加入牌九房间" + " " + member.unionCoin.toString());
        //             }
        //         } else if(res2.gt == "fkmj"){
        //             if(member.unionCoin < res2.data.score * 30){
        //                 throw new BusiError(500, "疲劳值不足不能加入麻将房间" + " " + member.unionCoin.toString());
        //             }
        //         } else if(member.unionCoin < res2.data.limitCoins){
        //             throw new BusiError(500, "疲劳值不足不能加入此房间");
        //         }
        //
        //         res2.member = member;
        //     }
        //
        // } else if (res2.clubID){  //// 如果是俱乐部房间，则需要鉴别用户是否俱乐部成员
        //     let member = await clubMemberDao.getByUID(uid, res2.clubID);
        //     if(!member){
        //         throw new BusiError(500, "非俱乐部成员不能加入此房间");
        //     }
        //
        //     if(res2.data.mode === 1){
        //         if(res2.gt == "fkpj"){
        //             if(member.coins < res2.data.score * 8){
        //                 throw new BusiError(500, "金币豆不足不能加入牌九房间" + " " + member.coins.toString());
        //             }
        //         } else if(res2.gt == "fkmj"){
        //             if(member.coins < res2.data.score * 30){
        //                 throw new BusiError(500, "金币豆不足不能加入麻将房间" + " " + member.coins.toString());
        //             }
        //         }else if(member.coins < res2.data.limitCoins){
        //             throw new BusiError(500, "金币豆不足不能加入此房间");
        //         }
        //     }
        //
        //     res2.member = member;
        // }

        // 游戏类型是否与房间类型匹配
        if (res2.gt !== gt) {
            throw new BusiError(500, "禁止非法进入房间");
        }

        // 房间是否有效
        let rti = await roomCacheService.isRoomAlive(roomID);
        if (!rti) {
            throw new BusiError(500, "房间已过期, 请重新创建房间");
        }

        // let creatorCacheKey = redisConst.GAME_ROOM_CREATOR_FLAG + ":" + uid;
        // let creatorInfo = await redis.get(creatorCacheKey);
        // if(creatorInfo && res2.owner === uid){
        //     if (creatorInfo.roomID === roomID) {
        //         if(creatorInfo.gt === gt){
        //             if(creatorInfo.ts === res2.ts){
        //                 await redis.set(roomTickerCacheKey, "1", 60 * 5); // 延长房间5分钟有效时间
        //                 return creatorInfo;
        //             }
        //         }
        //     }

        //     if (creatorInfo.roomID !== roomID) {
        //         let roomTickerCacheKey2 = redisConst.GAME_ROOM_TICKER_FLAG + ":" + uri.roomID;
        //         let rti2 = await redis.get(roomTickerCacheKey2);
        //         if (rti2){
        //             throw new BusiError(501, "玩家已经创建其他房间, 销毁房间前不能进入其他房间", creatorInfo);
        //         }
        //     }
        // }

        // 重复进入同一类型游戏同一房间则不做判断, 且自动延长房间5分钟有效时间
        let uri = await roomCacheService.getUserRoom(uid); // 获取用户房间信息
        if (uri) {
            if (uri.roomID === roomID) { // 同一个房间ID
                if (uri.gt === gt) { // 同一个房间类型， 直接进入房间
                    if (uri.ts === res2.ts) {
                        await roomCacheService.setRoomAliveEx(roomID);
                        return uri;
                    }
                }
            }

            if (uri.roomID !== roomID) { // 不同房间好
                let rti2 = await roomCacheService.isRoomAlive(uri.roomID); // 并且房间还存在， 则不允许进入其他房间
                if (rti2) {
                    throw new BusiError(501, "玩家已经在其他房间中, 结束游戏前不能进入其他房间 " + uri.roomID + " roomId=" + roomID, uri);
                }
            }

            // 清除失效房间信息
            await roomCacheService.userLeaveRoom(uid); // 其他情况则清除用户房间信息
        }
        // 如果是大联盟房间，则需要鉴别用户是否大联盟成员
        if (res2.unionID){
            let isFrost = await clubMemberDao.getFrostByUID(uid, res2.unionID);
            if(isFrost){
                throw new BusiError(500, "账号已经被冻结，请联系管理员");
            }
            let member = await clubMemberDao.getUnionByUID(uid, clubID, res2.unionID);
            if(!member){
                //如果不正确，说明玩家走链接进游戏,这个时候要选择一个币足够的俱乐部让玩家加入联盟里的游戏
                let member2 = await clubMemberDao.getEnoughUnionByUID(uid,res2.unionID,res2.data.limitCoins);
                if(!member2){
                    throw new BusiError(500, "疲劳值不足或非大联盟成员不能加入此房间");
                }
                /*if(member2.unionCoin < res2.data.limitCoins){
                    throw new BusiError(500, "疲劳值不足不能加入此房间");
                }*/

                if(res2.gt == "fkpj"){
                    let bei = 8;
                    if(res2.data.playMode === 2){
                        bei = bei * 2;
                    }
                    let limit = res2.data.limitCoins;
                    if (limit < res2.data.score * bei){
                        limit = res2.data.score * bei;
                    }
                    if(member2.unionCoin < limit){
                        throw new BusiError(500, "疲劳值不足不能加入牌九房间" + " " + member2.unionCoin.toString());
                    }
                } else if(res2.gt == "fkmj"){
                    let limit = res2.data.limitCoins;
                    if (limit < res2.data.score * 30){
                        limit = res2.data.score * 30;
                    }
                    if(member2.unionCoin < limit){
                        throw new BusiError(500, "疲劳值不足不能加入麻将房间" + " " + member2.unionCoin.toString());
                    }
                } else if(res2.gt == "fkzjh"){
                    let limit = res2.data.limitCoins;
                    if (limit < res2.data.score + 50){
                        limit = res2.data.score + 50;
                    }
                    if(member2.unionCoin < limit){
                        throw new BusiError(500, "疲劳值不足不能加入金花房间" + " " + member.unionCoin.toString());
                    }
                } else if(member2.unionCoin < res2.data.limitCoins){
                    throw new BusiError(500, "疲劳值不足不能加入此房间");
                }

                res2.member = member2;
            }else{
                if(res2.gt == "fkpj"){
                    let bei = 8;
                    if(res2.data.playMode === 2){
                        bei = bei * 2;
                    }
                    let limit = res2.data.limitCoins;
                    if (limit < res2.data.score * bei){
                        limit = res2.data.score * bei;
                    }
                    if(member.unionCoin < limit){
                        throw new BusiError(500, "疲劳值不足不能加入牌九房间" + " " + member.unionCoin.toString());
                    }
                } else if(res2.gt == "fkmj"){
                    let limit = res2.data.limitCoins;
                    if (limit < res2.data.score * 30){
                        limit = res2.data.score * 30;
                    }
                    if(member.unionCoin < limit){
                        throw new BusiError(500, "疲劳值不足不能加入麻将房间" + " " + member.unionCoin.toString());
                    }
                } else if(res2.gt == "fkzjh"){
                    let limit = res2.data.limitCoins;
                    if (limit < res2.data.score + 50){
                        limit = res2.data.score + 50;
                    }
                    if(member.unionCoin < limit){
                        throw new BusiError(500, "疲劳值不足不能加入麻将房间" + " " + member.unionCoin.toString());
                    }
                } else if(member.unionCoin < res2.data.limitCoins){
                    throw new BusiError(500, "疲劳值不足不能加入此房间");
                }

                res2.member = member;
            }
        } else if (res2.clubID){  //// 如果是俱乐部房间，则需要鉴别用户是否俱乐部成员
            let member = await clubMemberDao.getByUID(uid, res2.clubID);
            if(!member){
                throw new BusiError(500, "非俱乐部成员不能加入此房间");
            }

            if(res2.data.mode === 1){
                if(res2.gt == "fkpj"){
                    let limit = res2.data.limitCoins;
                    let bei = 8;
                    if(res2.data.playMode === 2){
                        bei = bei * 2;
                    }
                    if (limit < res2.data.score * bei){
                        limit = res2.data.score * bei;
                    }
                    if(member.coins < limit){
                        throw new BusiError(500, "金币豆不足不能加入牌九房间" + " " + member.coins.toString());
                    }
                } else if(res2.gt == "fkmj"){
                    let limit = res2.data.limitCoins;
                    if (limit < res2.data.score * 30){
                        limit = res2.data.score * 30;
                    }
                    if(member.coins < limit){
                        throw new BusiError(500, "金币豆不足不能加入麻将房间" + " " + member.coins.toString());
                    }
                }else if(res2.gt == "fkzjh"){
                    let limit = res2.data.limitCoins;
                    if (limit < res2.data.score + 50){
                        limit = res2.data.score + 50;
                    }
                    if(member.coins < limit){
                        throw new BusiError(500, "金币豆不足不能加入金花房间" + " " + member.coins.toString());
                    }
                } else if(member.coins < res2.data.limitCoins){
                    throw new BusiError(500, "金币豆不足不能加入此房间");
                }
            }
            res2.member = member;
        }


        // 进入房间
        await roomCacheService.userEnterRoom(uid, res2);

        // 设置房间玩家信息, 异步调用， 不需要同步
        this.setRoomUsersMap(uid, gt, roomID);
        //该子游戏在线人数增加一人
  //      await roomCacheService.addGameUserCount(gt, 1);

        //通知clubserver
        if (res2.unionID){
            let member = { msgType:59, unionId:res2.unionID };
            let s=JSON.stringify(member);
            await redis.publishUnion(s);
        }else if (res2.clubID){
            let member = { msgType:60, clubId:res2.clubID };
            let s=JSON.stringify(member);
            await redis.publishUnion(s);
        }
        return res2;
    }

    /**
     * 设置房间用户列表信息
     * @param {String} uid 
     * @param {String} gt 
     * @param {String} roomID 
     */
    async setRoomUsersMap(uid, gt, roomID){
        try{
            let user = await userDao.getUserInfoByUid(uid);
            await roomCacheService.setRoomUsersMap(uid, gt, roomID, user);
        }catch(e){
            console.log("set room users map err:", e);
        }
    }

    /**
     * 离开房间，只要用户离开房间，则该子游戏在线人数减一人
     * @param {String} uid 
     * @param {String} roomID 
     * @param {String} gt
     */
    async leaveRoom(uid, roomID, gt) {
        // 房间类型是否匹配
        let res2 = await roomCacheService.getRoom(roomID); // 缓存最长保留时间为24h
        if (res2 && res2.gt !== gt) {
            throw new BusiError(500, "房间类型不匹配");
        }

        // 是否存在房间信息
        let uri = await roomCacheService.getUserRoom(uid);
        if (uri) {
            // 房间号一致
            if (uri.roomID === roomID) {
                // 房间类型一致
                if (uri.gt === gt) {
                    await roomCacheService.removeUserRoom(uid);
                    roomCacheService.removeRoomUserFromMap(uid, gt, roomID);
 //                   await roomCacheService.addGameUserCount(gt, -1);
                    if (res2.unionID){
                        let member = { msgType:59, unionId:res2.unionID };
                        let s=JSON.stringify(member);
                        await redis.publishUnion(s);
                    }else if (res2.clubID){
                        let member = { msgType:60, clubId:res2.clubID };
                        let s=JSON.stringify(member);
                        await redis.publishUnion(s);
                    }
                    return;
                }

                // 房间是否有效
                let rti = await roomCacheService.isRoomAlive(uri.roomID);
                if (!rti) {
                    await roomCacheService.removeUserRoom(uid);
                    roomCacheService.removeRoomUserFromMap(uid, gt, roomID);
                   // await roomCacheService.addGameUserCount(gt, -1);
                    if (res2.unionID){
                        let member = { msgType:59, unionId:res2.unionID };
                        let s=JSON.stringify(member);
                        await redis.publishUnion(s);
                    }else if (res2.clubID){
                        let member = { msgType:60, clubId:res2.clubID };
                        let s=JSON.stringify(member);
                        await redis.publishUnion(s);
                    }
                    return;
                }

                throw new BusiError(500, "玩家还未退出游戏, 不能离开房间 " + uri.roomID + " roomId=" + roomID);
            }


            // 房间号不一致处理
            // 房间失效则允许清理离开房间, 否则不能离开房间
            let rti = await roomCacheService.isRoomAlive(uri.roomID);
            if (!rti) {
                await roomCacheService.removeUserRoom(uid);
                roomCacheService.removeRoomUserFromMap(uid, gt, roomID);
               // await roomCacheService.addGameUserCount(gt, 1);
                if (res2.unionID){
                    let member = { msgType:59, unionId:res2.unionID };
                    let s=JSON.stringify(member);
                    await redis.publishUnion(s);
                }else if (res2.clubID){
                    let member = { msgType:60, clubId:res2.clubID };
                    let s=JSON.stringify(member);
                    await redis.publishUnion(s);
                }
                return;
            }

            throw new BusiError(500, "玩家尚未结束游戏, 不能离开房间");
        }
    }

    /**
     * 游戏启动上报信息
     * @param {String} gt 
     * @param {String} host 
     * @param {String} port 
     * @param {String} path 
     * @param {String} scheme 
     */
    async gameStartup(gt, host, port, path, scheme) {
        let cacheKey = redisConst.GAME_STARTUP_FLAG + ":" + gt;
        let res = await redis.set(cacheKey, { gt: gt, ts: new Date().getTime(), host: host, port: port, path: path, scheme: scheme });
        if (!res) {
            throw new BusiError(500, "游戏启动信息设置失败");
        }
    }

    /**
     * 获取游戏服信息
     * @param {String} gt 
     */
    async getGameServerInfo(gt) {
        let cacheKey = redisConst.GAME_STARTUP_FLAG + ":" + gt;
        let res = await redis.get(cacheKey);
        if (!res) {
            console.logw("获取游戏服信息失败, 该类型游戏服不存在, GT:", gt);
            throw new BusiError(500, "获取游戏服信息失败, 该游戏类型游戏服不存在");
        }

        // 游戏是否存活
        let gameTickerKey = redisConst.GAME_TICKER_FLAG + ":" + gt;
        let res2 = await redis.get(gameTickerKey);
        if (!res2) {
            throw new BusiError(500, "当前游戏维护中，请选择其他游戏");
        }

        return res;
    }

    /**
    * 
    * @param {String} gt 
    */
    async gameTicker(gt) {
        let cacheKey = redisConst.GAME_TICKER_FLAG + ":" + gt;
        let res = await redis.set(cacheKey, "1", 120);
        if (!res) {
            throw new BusiError(500, "游戏心跳信息设置失败");
        }
    }

    //快三 游戏结束存储消息
    async gameOverToSave(myBody) {
        let sql = 'insert into admin_gameover (playNum,gameType,indexId,allBetCoins,allBetNum,windBetNum,loseBetNum,winCoins,payCoins,loseCoins,ProfitCoins) values(?,?,?,?,?,?,?,?,?,?,?)';
        let args = [myBody.playNum,myBody.gameType,myBody.indexId,myBody.allBetCoins,myBody.allBetNum,myBody.windBetNum,myBody.loseBetNum,myBody.winCoins,myBody.payCoins,myBody.loseCoins,myBody.ProfitCoins];
        let res = await mysqlPool.query(sql, args);
        if (res) {
            return res.insertId;
        }

        return 0;
    }
    
    //保存玩家输赢消息
    // gameType int32,//玩法类型 北京还是江苏
	// indexId string,//期号
	// playId int,	//id
	// playName string,//名字
	// betList	 string,//下注类型及金额以及该注输赢 map->json->string
	// follow	string,//开奖结果		arr->json->string
	// winCoins float32) bool {//玩家输赢
    async playerBetInfo(myBody) {
        let sql = 'insert into admin_playerWin (gameType,indexId,uid,name,betList,follow,winCoins,createTime) values(?,?,?,?,?,?,?,NOW())';
        let args = [myBody.gameType,myBody.indexId,myBody.playId,myBody.playName,myBody.betList,myBody.follow,myBody.winCoins];
        let res = await mysqlPool.query(sql, args);
        if (res) {
            return res.insertId;
        }

        return 0;
    }

    /**
     * 获取游戏标志位
     * @param {String} uid 
     * @param {String} gt 
     * @param {Number} rt 1, 2, 3, 4
     */
    async getGameFlagGaming(uid, gt) {
        let userCacheKey = redisConst.USER_GAMING_FLAG + ":" + uid;
        let userGameRes = await redis.get(userCacheKey);
        if (userGameRes) {
            // 如果存在游戏标识， 是否游戏标识游戏
            // if (userGameRes.gt !== gt) {
            // 表示游戲是否啓動
            let gamingGameFlagKey = redisConst.GAME_STARTUP_FLAG + ":" + userGameRes.gt;
            let gamingGameRes = await redis.get(gamingGameFlagKey);

            // 如果存在游戏标识， 不是游戏标识游戏， 是否游戏时间戳过时
            if (gamingGameRes && userGameRes.ts > gamingGameRes.ts) {
                // 游戏是否存活
                let userGameTickerKey = redisConst.GAME_TICKER_FLAG + ":" + userGameRes.gt;
                let userGameTickerRes = await redis.get(userGameTickerKey);
                if (userGameTickerRes) {
                    throw new BusiError(501, "游戏中", { gt: userGameRes.gt, rt: userGameRes.rt });
                }
            }
            // }
        }

        // 游戏是否存活
        let gameTickerKey = redisConst.GAME_TICKER_FLAG + ":" + gt;
        let res = await redis.get(gameTickerKey);
        if (!res) {
            throw new BusiError(500, "当前游戏维护中，请选择其他游戏");
        }
    }

    /**
     * 设置游戏标志位
     * @param {String} uid 
     * @param {String} gt 
     * @param {Number} rt 1, 2, 3, 4
     */
    async setGameFlagGaming(uid, gt, rt) {
        const limitKey = redisConst.USER_GAMING_FLAG_LIMIT + ":" + uid;
        let ok = await redis.lock(limitKey, 1, 10);

        try {
            // 是否存在游戏标识
            let userCacheKey = redisConst.USER_GAMING_FLAG + ":" + uid;
            let userGameRes = await redis.get(userCacheKey);
            if (userGameRes) {
                // 表示游戲是否啓動
                let gamingGameFlagKey = redisConst.GAME_STARTUP_FLAG + ":" + userGameRes.gt;
                let gamingGameRes = await redis.get(gamingGameFlagKey);

                // 如果存在游戏标识， 是否游戏标识游戏
                if (userGameRes.gt !== gt) {
                    if (!ok) { // 不是同一個游戲限制并發請求
                        throw new BusiError(500, "请求太频繁");
                    }

                    // 如果存在游戏标识， 不是游戏标识游戏， 是否游戏时间戳过时
                    if (gamingGameRes && userGameRes.ts > gamingGameRes.ts) {
                        // 游戏是否存活
                        let userGameTickerKey = redisConst.GAME_TICKER_FLAG + ":" + userGameRes.gt;
                        let userGameTickerRes = await redis.get(userGameTickerKey);
                        if (userGameTickerRes) {
                            throw new BusiError(500, "当前正在其它游戏中");
                        }
                    }
                }

                if (userGameRes.gt === gt && userGameRes.rt !== rt) {
                    if (gamingGameRes && userGameRes.ts > gamingGameRes.ts) {
                        throw new BusiError(500, "当前该游戏其他类型房间游戏，游戏中不能同时进入不同类型游戏房间");
                    }
                }
            }

            // 游戏是否存活
            let gameTickerKey = redisConst.GAME_TICKER_FLAG + ":" + gt;
            let res = await redis.get(gameTickerKey);
            if (!res) {
                throw new BusiError(500, "当前游戏系统故障，请选择其他游戏");
            }

            await redis.set(userCacheKey, { gt: gt, ts: new Date().getTime(), rt: rt }, 60 * 60);

        } finally {
            await redis.del(limitKey);
        }
    }

    /**
     * 清除游戏标志位
     * @param {String} uid 
     * @param {String} gt 
     */
    async clearGameGamingFlag(uid, gt) {
        let userCacheKey = redisConst.USER_GAMING_FLAG + ":" + uid;
        let userGameRes = await redis.get(userCacheKey);

        if (!userGameRes) {
            throw new BusiError(500, "cache:" + userCacheKey + " not exists!");
        }

        if (userGameRes.gt === gt) {
            await redis.del(userCacheKey);
        }
    }

    /**
     *
     * @param info
     * @param {String} gameType
     * @param {Number} cnt
     * @returns {Promise<void>}
     */
    async updateOnlineUserCnt(gameType, cnt) {
        let cacheKey = redisConst.GAME_ONLINE_USER_CNT_KEY + ":" + gameType;
        try {
            let res = await redis.set(cacheKey, cnt, 120);
            if (!res) {
                throw new BusiError(500, "redis update online user cnt err");
            }
        } catch (e) {
            console.logw("append user game info err:", e);
            throw e;
        }
    }

    /**
    *
    * @param {String} gameType
    * @returns {Promise<void>}
    */
    async getOnlineUserCnt(gameType) {
        let cacheKey = redisConst.GAME_ONLINE_USER_CNT_KEY + ":" + gameType;
        try {
            let res = await redis.get(cacheKey);
            if (!res) {
                return -1;
            }

            return res;
        } catch (e) {
            console.logw("append user game info err:", e);
            throw e;
        }
    }

    async updateRoomUsers(roomId, usersInfo) {
        let cacheKey = redisConst.ROOM_USERSINFO + ":" + roomId;
        await redis.set(cacheKey, usersInfo);
    }

    async getRoomUsers(roomId, uid) {
        let cacheKey = redisConst.ROOM_USERSINFO + ":" + roomId;
        let info = await redis.get(cacheKey);
        let roomInfo = await this.getFkGameRoomInfo(roomId);
        if (info) {
            info.roomInfo = roomInfo;
        } else {
            info = {};
            info.roomInfo = roomInfo;
        }
        if (roomInfo && roomInfo.data && roomInfo.data.onlyFriend === 1) {
            let bFriend = await friendService.isFriend(uid, roomInfo.owner);
            info.bFriend = bFriend;
        }

        return info;
    }

    async getClubBill(uid, clubID,page) {
       let res = await clubCardsUpdateRecordDao.GetBill(uid, clubID,page);
       return res;
    }

    async getUserCount(){
        return await roomCacheService.getGameUserCount();
    }
}

let gameService = new GameService();
module.exports = gameService;